package demo.java.util.concurrent.locks;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.StampedLock;

/**
 * Lock 框架是锁定的一个抽象，它允许把锁定的实现作为 Java 类，而不是作为语言的特性来实现。这就为 Lock
 * 的多种实现留下了空间，各种实现可能有不同的调度算法、性能特性或者锁定语义。
 * <p>
 * Lock 和 synchronized 有一点明显的区别 —— lock 必须在 finally
 * 块中释放。否则，如果受保护的代码将抛出异常，锁就有可能永远得不到释放！
 * <p>
 * <li>Lock类也可以实现线程同步，而Lock获得锁需要执行lock方法，释放锁需要执行unLock方法
 * <li>Lock类可以创建Condition对象，Condition对象用来是线程等待和唤醒线程，
 * 需要注意的是Condition对象的唤醒的是用同一个Condition执行await方法的线程，所以也就可以实现唤醒指定类的线程
 * <li>Lock类分公平锁和不公平锁，公平锁是按照加锁顺序来的，非公平锁是不按顺序的，也就是说先执行lock方法的锁不一定先获得锁
 * <li>Lock类有读锁和写锁，读读共享，写写互斥，读写互斥
 */
public class LockDemo {

    void demo() {

        Lock lock = new ReentrantLock();

        /** 可重入锁 */
        ReentrantLock reentrantLock = new ReentrantLock();

        /** 读写锁 */
        ReadWriteLock readWriteLock = new ReentrantReadWriteLock();

        /** 锁 */
        StampedLock stampedLock = new StampedLock();

        Condition condition = lock.newCondition();

    }

    /**
     * ReentrantLock，意思是“可重入锁”，ReentrantLock是唯一实现了Lock接口的类
     */
    public void testReentrantLock() {

    }

    /**
     * 通知与锁定之间有一个交互 —— 为了在对象上 wait 或 notify ，您必须持有该对象的锁。就像 Lock 是同步的概括一样， Lock
     * 框架包含了对 wait 和 notify 的概括，这个概括叫作 条件（Condition） 。 Lock
     * 对象则充当绑定到这个锁的条件变量的工厂对象，与标准的 wait 和 notify 方法不同，对于指定的 Lock
     * ，可以有不止一个条件变量与它关联。这样就简化了许多并发算法的开发。
     */
    void demoCondition() {
        BoundedBuffer buffer = new BoundedBuffer();
    }

    /**
     * <li>lock()方法是平常使用得最多的一个方法，就是用来获取锁。如果锁已被其他线程获取，则进行等待。
     * <li>lockInterruptibly()，如果线程正在等待获取锁，则这个线程能够响应中断，即中断线程的等待状态
     * <li>tryLock()能获得锁就返回true，不能就立即返回false，也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待。
     * <li>tryLock(long timeout,TimeUnit unit)，可以增加时间限制，如果超过该时间段还没获得锁，返回false
     * <li>unLock()方法是用来释放锁的
     */
    void demoLock() {
        Lock lock = new ReentrantLock();
        /** lock() 能获得锁就返回，不能的话一直等待获得锁 */
        {
            lock.lock();
            try {

            } finally {
                lock.unlock();
            }
        }

        /** lock和lockInterruptibly，如果两个线程分别执行这两个方法，但此时中断这两个线程，前者不会抛出异常，而后者会抛出异常 */
        {
            try {
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

        /** tryLock能获得锁就返回true，不能就立即返回false， */
        {
            if (lock.tryLock()) {
                try {
                    // manipulate protected state
                } finally {
                    lock.unlock();
                }
            } else {
                // perform alternative actions
            }
        }
        /** tryLock(long timeout,TimeUnit unit)，可以增加时间限制，如果超过该时间段还没获得锁，返回false */
        {
            try {
                if (lock.tryLock(30, TimeUnit.SECONDS)) {
                    try {
                        // manipulate protected state
                    } finally {
                        lock.unlock();
                    }
                } else {
                    // perform alternative actions
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

/**
 * 类 Object 包含某些特殊的方法，用来在线程的 wait() 、 notify() 和 notifyAll() 之间进行通信。这些是高级的并发性特性。
 * <p>
 * 通知与锁定之间有一个交互————为了在对象上 wait 或 notify ，您必须持有该对象的锁。 就像 Lock 是同步的概括一样， Lock
 * 框架包含了对 wait 和 notify 的概括，这个概括叫作条件（Condition） 。 Lock对象则充当绑定到这个锁的条件变量的工厂对象，与标准的
 * wait 和 notify 方法不同，对于指定的 Lock ，可以有不止一个条件变量与它关联。这样就简化了许多并发算法的开发。
 *
 * <li>通过创建Condition对象来使线程wait，必须先执行lock.lock方法获得锁
 * <li>condition对象的signal方法可以唤醒wait线程
 *
 * <h2>Condition类和Object类</h2>
 * <li>Condition类的awiat方法和Object类的wait方法等效
 * <li>Condition类的signal方法和Object类的notify方法等效
 * <li>Condition类的signalAll方法和Object类的notifyAll方法等效
 *
 * <h2>创建多个condition对象</h2>
 * <li>一个condition对象的signal（signalAll）方法和该对象的await方法是一一对应的，也就是一个condition对象的signal（signalAll）方法不能唤醒其他condition对象的await方法
 * <li>ReentrantLock类可以唤醒指定条件的线程，而object的唤醒是随机的
 */
class BoundedBuffer {

    final Lock lock = new ReentrantLock();
    /**
     * 为该ReentrantLock设置了两个Condition
     * ,这里notEmpty和notFull作为lock的两个条件是可以分别负责管理想要加入元素的线程和想要取出元素的线程。
     */
    final Condition notFull = lock.newCondition();
    final Condition notEmpty = lock.newCondition();

    final Object[] items = new Object[100];
    int putptr, takeptr, count;

    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length) {
                notFull.await();
            }
            items[putptr] = x;
            if (++putptr == items.length) {
                putptr = 0;
            }
            ++count;
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                // 这里针对notEmpty这个condition，如果队列为空则线程等待这个条件
                notEmpty.await();
            }

            Object x = items[takeptr];
            if (++takeptr == items.length) {
                takeptr = 0;
            }
            --count;
            notFull.signal();// 这里针对notFull这个condition，唤醒因该条件而等待的线程
            return x;
        } finally {
            lock.unlock();
        }
    }
}
